package coreutil

import (
	"core/config"
	"fmt"
	"github.com/go-redis/redis"
	"net"
	"time"
)

var (
	RedisUtil *redis.Client
)

//
//func Exists(keys ...string) bool {
//	result, err := RedisUtil.Exists(keys...).Result()
//	if err != nil {
//		log.Fatal(err)
//		return false
//	}
//	return result == 1
//}
//
//// Expire 设置key的过期时间
//func Expire(key string, expiration int) {
//	RedisUtil.Expire(key, time.Duration(expiration)*time.Second)
//}
//
//func Keys(pattern string) ([]string, error) {
//	return RedisUtil.Keys(pattern).Result()
//}
//
//// Set : expiration=0表示无过期时间
//func SetStr(key string, value any, expiration int) {
//	err := RedisUtil.Set(key, value, time.Duration(expiration)*time.Second).Err()
//	if err != nil {
//		panic(err)
//	}
//}
//
//func Get(key string) (string, error) {
//	return RedisUtil.Get(key).Result()
//}
//
//func Del(keys ...string) bool {
//	result, err := RedisUtil.Del(keys...).Result()
//	if err != nil {
//		return false
//	}
//	return result == 1
//}
//
//// SetIfAbsent 不存在时才设置（新增操作）SetNX
//func SetIfAbsent(key string, value any, expiration int) bool {
//	result, err := RedisUtil.SetNX(key, value, time.Duration(expiration)*time.Second).Result()
//	if err != nil {
//		log.Fatal(err)
//		return false
//	} // name存在，不会修改
//	return result
//}
//
//// SetIfPresent 存在时才设置（修改操作）SetXX
//func SetIfPresent(key string, value any, expiration int) bool {
//	result, err := RedisUtil.SetXX(key, value, time.Duration(expiration)*time.Second).Result()
//	if err != nil {
//		log.Fatal(err)
//		return false
//	}
//	return result
//}
//
//// Incr Incr函数每次加一
//func Incr(key string) int64 {
//	result, err := RedisUtil.Incr(key).Result()
//	if err != nil {
//		panic(err)
//	}
//	return result
//}
//
//func IncrBy(key string, value int64) int64 {
//	result, err := RedisUtil.IncrBy(key, value).Result()
//	if err != nil {
//		panic(err)
//	}
//	return result
//}
//
//func IncrByFloat(key string, value float64) float64 {
//	result, err := RedisUtil.IncrByFloat(key, value).Result()
//	if err != nil {
//		panic(err)
//	}
//	return result
//}
//
//func Decr(key string) int64 {
//	result, err := RedisUtil.Decr(key).Result()
//	if err != nil {
//		panic(err)
//	}
//	return result
//}
//
//func DecrBy(key string, value int64) int64 {
//	result, err := RedisUtil.DecrBy(key, value).Result()
//	if err != nil {
//		panic(err)
//	}
//	return result
//}
//
//// MGet MGet函数可以传入任意个key，一次性返回多个值。
//// 这里Result返回两个值，第一个值是一个数组，第二个值是错误信息
//func MGet(keys ...string) []interface{} {
//	result, err := RedisUtil.MGet(keys...).Result()
//	if err != nil {
//		log.Fatal(err)
//		return nil
//	}
//	return result
//}
//
//func StrLen(key string) (int64, error) {
//	return RedisUtil.StrLen("name").Result()
//}
//
//// RPush 列表右侧插入o(1~n)
//func RPush(key string, value any) {
//	RedisUtil.RPush(key, value)
//}
//
//// LPush 列表左侧插入
//func LPush(key string, value any) {
//	RedisUtil.LPush(key, value)
//}
//
//// LInsertBefore #从元素value前插入newValue 时间复杂度o(n) ，需要遍历列表
//func LInsertBefore(key string, pivot, value any) {
//	RedisUtil.LInsertBefore(key, pivot, value)
//}
//
//// LInsertAfter #从元素value后插入newValue 时间复杂度o(n) ，需要遍历列表
//func LInsertAfter(key string, pivot, value any) {
//	RedisUtil.LInsertAfter(key, pivot, value)
//}
//
//// LPop 从列表左侧弹出一个item 时间复杂度o(1)
//func LPop(key string) (string, error) {
//	return RedisUtil.LPop(key).Result()
//
//}
//
//// RPop 从列表右侧弹出一个item 时间复杂度o(1)
//func RPop(key string) (string, error) {
//	return RedisUtil.RPop(key).Result()
//}
//
//// LRem 根据count值，从列表中删除所有value相同的项 时间复杂度o(n)
//// 1 count>0 从左到右，删除最多count个value相等的项
//// 2 count<0 从右向左，删除最多 Math.abs(count)个value相等的项
//// 3 count=0 删除所有value相等的项
//func LRem(key string, count int64, value any) {
//	RedisUtil.LRem(key, count, value)
//}
//
//// LTrim 按照索引范围修剪列表 o(n),保留开始到结束范围
//func LTrim(key string, start, stop int64) {
//	RedisUtil.LTrim(key, start, stop)
//}
//
//// LRange 包含stop获取列表指定索引范围所有item  o(n) 从start到stop
//func LRange(key string, start, stop int64) ([]string, error) {
//	return RedisUtil.LRange(key, start, stop).Result()
//}
//
//// LIndex 获取列表指定索引的item  o(n)
//func LIndex(key string, index int64) (string, error) {
//	return RedisUtil.LIndex(key, index).Result()
//}
//
//// LLen 获取列表长度
//func LLen(key string) (int64, error) {
//	return RedisUtil.LLen(key).Result()
//}
//
//// LSet 设置列表指定索引值为value o(n)
//func LSet(key string, index int64, value interface{}) {
//	RedisUtil.LSet(key, index, value)
//}
//
//// HGet 获取hash key对应的field的value 时间复杂度为 o(1)
//func HGet(key, field string) (string, error) {
//	return RedisUtil.HGet(key, field).Result()
//}
//
//// HGetAll 返回hash key 对应的所有field和value  时间复杂度是o(n)
//func HGetAll(key string) (map[string]string, error) {
//	return RedisUtil.HGetAll(key).Result()
//}
//
//// HVals 返回hash key 对应的所有field的value  时间复杂度是o(n)
//func HVals(key string) ([]string, error) {
//	return RedisUtil.HVals(key).Result()
//}
//
//// HKeys 返回hash key对应的所有field  时间复杂度是o(n)
//func HKeys(key string) ([]string, error) {
//	return RedisUtil.HKeys(key).Result()
//}
//
//func HIncrBy(key, field string, incr int64) (int64, error) {
//	return RedisUtil.HIncrBy(key, field, incr).Result()
//}
//
//// HSet 设置hash key对应的field的value值 时间复杂度为 o(1)
//func HSet(key, field string, value interface{}) {
//	RedisUtil.HSet(key, field, value)
//}
//
//// HDel 删除hash key对应的field的值 时间复杂度为 o(1)
//func HDel(key string, fields ...string) {
//	RedisUtil.HDel(key, fields...)
//}
//
//func HExists(key, field string) bool {
//	result, err := RedisUtil.HExists(key, field).Result()
//	if err != nil {
//		log.Fatal(err)
//		return false
//	}
//	return result
//}
//
//// HLen 获取hash key field的数量  时间复杂度为 o(1)
//func HLen(key string) (int64, error) {
//	return RedisUtil.HLen(key).Result()
//}
//
//// HMGet 批量获取hash key 的一批field对应的值  时间复杂度是o(n)
//func HMGet(key string, fields ...string) ([]interface{}, error) {
//	return RedisUtil.HMGet(key, fields...).Result()
//}
//
//// HMSet 批量设置hash key的一批field value 时间复杂度是o(n)
//func HMSet(key string, fields map[string]interface{}) {
//	RedisUtil.HMSet(key, fields)
//}
//
//// SAdd 向集合key添加element（如果element存在，添加失败） o(1)
//func SAdd(key string, members ...interface{}) {
//	RedisUtil.SAdd(key, members...)
//}
//
//// SRem 从集合中的element移除掉 o(1)
//func SRem(key string, members ...interface{}) {
//	RedisUtil.SRem(key, members...)
//}
//
//// SCard 计算集合大小
//func SCard(key string) (int64, error) {
//	return RedisUtil.SCard(key).Result()
//}
//
//// SIsMember 判断element是否在集合中
//func SIsMember(key string, member interface{}) bool {
//	result, err := RedisUtil.SIsMember(key, member).Result()
//	if err != nil {
//		log.Fatal(err)
//		return false
//	}
//	return result
//}
//
//// Subscribe 订阅消息
//// // 读取channel消息
//// iface, err := sub.Receive()
////
////	if err != nil {
////	   // handle error
////	}
////
//// // 检测收到的消息类型
//// switch iface.(type) {
//// case *redis.Subscription:
////
////	// 订阅成功
////
//// case *redis.Message:
////
////	   // 处理收到的消息
////	   // 这里需要做一下类型转换
////	   m := iface.(redis.Message)
////	   // 打印收到的消息
////		fmt.Println(m.Payload)
////
//// case *redis.Pong:
////
////	// 收到Pong消息
////
//// default:
////
////	// handle error
////
//// //sub.Channel() 返回go channel，可以循环读取redis服务器发过来的消息
////
//// for msg := range sub.Channel() {
////
//// // 打印收到的消息
////
//// fmt.Println(msg.Channel)
////
//// fmt.Println(msg.Payload)
//// }
//// // 取消订阅
////
//// sub.Unsubscribe("channel1")
//func Subscribe(channels ...string) *redis.PubSub {
//	return RedisUtil.Subscribe(channels...)
//}
//
//// PSubscribe 用法跟Subscribe一样，区别是PSubscribe订阅通道(channel)支持模式匹配。
//func PSubscribe(channels ...string) *redis.PubSub {
//	return RedisUtil.PSubscribe(channels...)
//}
//
//// Publish 将消息发送到指定的channel
//func Publish(channel string, message interface{}) {
//	err := RedisUtil.Publish(channel, message).Err()
//	if err != nil {
//		panic(err)
//	}
//}
//
//// PubSubChannels 查询活跃的channel
//// pattern "user_*"
//func PubSubChannels(pattern string) []string {
//	// 没有指定查询channel的匹配模式，则返回所有的channel
//	chs, err := RedisUtil.PubSubChannels(pattern).Result()
//	if err != nil {
//		panic(err)
//	}
//	return chs
//}
//
//// PubSubNumSub 查询指定的channel有多少个订阅者
////
////	for ch, count := range chs {
////		fmt.Println(ch) // channel名字
////		fmt.Println(count) // channel的订阅者数量
////	}
//func PubSubNumSub(channels ...string) map[string]int64 {
//	chs, err := RedisUtil.PubSubNumSub(channels...).Result()
//	if err != nil {
//		panic(err)
//	}
//	return chs
//}
//
//// TxPipeline 以Pipeline的方式操作事务
////
//// // 通过Exec函数提交redis事务
////
//// _, err := pipe.Exec()
//func TxPipeline() redis.Pipeliner {
//	return RedisUtil.TxPipeline()
//}
//
//// Watch redis乐观锁支持，可以通过watch监听一些Key, 如果这些key的值没有被其他人改变的话，才可以提交事务。
//// // 定义一个回调函数，用于处理事务逻辑
////
////	fn := func(tx *redis.Tx) error {
////			// 先查询下当前watch监听的key的值
////			v, err := tx.Get("key").Result()
////			if err != nil && err != redis.Nil {
////				return err
////			}
////
////			// 这里可以处理业务
////			fmt.Println(v)
////
////			// 如果key的值没有改变的话，Pipelined函数才会调用成功
////			_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
////				// 在这里给key设置最新值
////				pipe.Set("key", "new value", 0)
////				return nil
////			})
////			return err
////		}
////
//// // 使用Watch监听一些Key, 同时绑定一个回调函数fn, 监听Key后的逻辑写在fn这个回调函数里面
//// // 如果想监听多个key，可以这么写：client.Watch(fn, "key1", "key2", "key3")
//// client.Watch(fn, "key")
//func Watch(fn func(tx *redis.Tx) error, keys ...string) {
//	err := RedisUtil.Watch(fn, keys...)
//	if err != nil {
//		panic(err)
//	}
//}

/*
*
写入字符串
*/
func RedisSetStr(key string, value any, second int) {
	err := RedisUtil.Set(key, value, time.Duration(second)*time.Second).Err()
	if err != nil {
		panic(err)
	}
}

/*
*
获取字符串
*/
func Get(key string) (string, error) {
	return RedisUtil.Get(key).Result()
}

/*
*
写入字符串
*/
func RedisSetStruct(key string, value interface{}, second int) {
	var buff, err = Json.Marshal(value)
	if err != nil {
		panic(err)
	}
	err = RedisUtil.Set(key, buff, time.Duration(second)*time.Second).Err()
	if err != nil {
		panic(err)
	}
}

/*
*
获取字符串
*/
func RedisGetStruct(key string, value interface{}) error {
	buff, err := RedisUtil.Get(key).Bytes()
	if err != nil {
		return err
	}
	err = Json.Unmarshal(string(buff), value)
	if err != nil {
		return err
	}
	return nil
}

func InitRedis() *redis.Client {
	var cfg = config.Config.Redis
	var rdb = redis.NewClient(&redis.Options{Addr: cfg.Addr,
		Password: cfg.Password, DB: cfg.DB,
		//连接池容量及闲置连接数量
		//go-redis包自带了连接池，会自动维护redis连接，因此创建一次client即可，不要查询一次redis就关闭client。
		//PoolSize:     15, // 连接池最大socket连接数，默认为4倍CPU数， 4 * runtime.NumCPU
		//MinIdleConns: 10, //在启动阶段创建指定数量的Idle连接，并长期维持idle状态的连接数不少于指定数量；。
		//超时
		DialTimeout:  5 * time.Second, //连接建立超时时间，默认5秒。
		ReadTimeout:  3 * time.Second, //读超时，默认3秒， -1表示取消读超时
		WriteTimeout: 3 * time.Second, //写超时，默认等于读超时
		PoolTimeout:  4 * time.Second, //当所有连接都处在繁忙状态时，客户端等待可用连接的最大等待时长，默认为读超时+1秒。
		//闲置连接检查包括IdleTimeout，MaxConnAge
		IdleCheckFrequency: 60 * time.Second, //闲置连接检查的周期，默认为1分钟，-1表示不做周期性检查，只在客户端获取连接时对闲置连接进行处理。
		IdleTimeout:        5 * time.Minute,  //闲置超时，默认5分钟，-1表示取消闲置超时检查
		MaxConnAge:         0 * time.Second,  //连接存活时长，从创建开始计时，超过指定时长则关闭连接，默认为0，即不关闭存活时长较长的连接
		//命令执行失败时的重试策略
		MaxRetries:      0,                      // 命令执行失败时，最多重试多少次，默认为0即不重试
		MinRetryBackoff: 8 * time.Millisecond,   //每次计算重试间隔时间的下限，默认8毫秒，-1表示取消间隔
		MaxRetryBackoff: 512 * time.Millisecond, //每次计算重试间隔时间的上限，默认512毫秒，-1表示取消间隔
		//可自定义连接函数
		Dialer: func() (net.Conn, error) {
			netDialer := &net.Dialer{
				Timeout:   5 * time.Second,
				KeepAlive: 5 * time.Minute,
			}
			return netDialer.Dial("tcp", "127.0.0.1:9879")
		},
		//钩子函数
		OnConnect: func(conn *redis.Conn) error { //仅当客户端执行命令时需要从连接池获取连接时，如果连接池需要新建连接时则会调用此钩子函数
			fmt.Printf("redis从连接池获取链接%v\n", conn)
			return nil
		},
	})
	res, err := rdb.Ping().Result()
	if err != nil {
		fmt.Println("ping 出错：", err)
	}
	fmt.Println("redis:" + res)
	//工具
	RedisUtil = rdb
	return RedisUtil
}
